#!/usr/bin/env python3
#
# Copyright (c) [2020] Huawei Technologies Co., Ltd. All rights reserved.
#
# Licensed under the Mulan Permissive Software License v2.
# You can use this software according to the terms and conditions of the MulanPSL - 2.0.
# You may obtain a copy of MulanPSL - 2.0 at:
#
#     https://opensource.org/licenses/MulanPSL-2.0
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
# FIT FOR A PARTICULAR PURPOSE.
# See the MulanPSL - 2.0 for more details.
#

"""
mpl-collect-groot-lists: Collect all GC roots from the `.groots.txt` lists
generated by mplcg from `.mpl` files.  The GC roots are important for tracing GC
collectors and the backup tracer currently used in RC collector for cleaning up
purpose.

The `.groots.txt` files are generated from each `.mpl` file using `mplcg`.

    mplcg --quiet blah.mpl

That will generate `blah.groots.txt`

The current version of `mplcg` generates the `.groots.txt` file by default.

If you are building an application:

1.  Compile each `.mpl` file with `mplcg`.  Each `xxx.mpl` file will yield a
    `xxx.groots.txt` file.

2.  After processing all `.mpl` files, you can generate the `unified.groots.s`
    file which contains an array of pointers to GC roots:

    mpl-collect-groot-lists file1.groots.txt file2.groots.txt file3.groots.txt

    That will generate the `unified.groots.s` file.

3.  Assemble and link the `unified.groots.s` file into the executable or shared
    object you are building.

See zeiss/prebuilt/tools/build.template for an example.

If you are writing tests which do not have GC roots, you can create the
`unified.groots.s` file from an empty file, such as `/dev/null`:

    mpl-collect-groot-lists /dev/null
"""

import argparse
import os.path
import sys

DEFAULT_OUTPUT_FILE = "unified.groots.s"
LIST_NAME = "__MRT_GCRootList"
LIST_SIZE_NAME = "__MRT_GCRootListSize"

def log(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

parser = argparse.ArgumentParser(description='''
Collect .groots.txt files, de-duplicate, and generate an assembly file, usually
"unified.groots.s", which contains an array of pointers to all GC roots.

Curently only designed for AArch64.
''')

parser.add_argument('files', metavar='FILE',
        type=str, nargs='+',
        help='File (xxxx.groots.txt) to be concatenated.')
parser.add_argument('-o', '--output', metavar="OUTFILE",
        type=str, default=DEFAULT_OUTPUT_FILE,
        help='Write to OUTFILE.  Default: {}'.format(DEFAULT_OUTPUT_FILE))

args = parser.parse_args()

for filename in args.files:
    if not os.path.exists(filename):
        print("{}: Not a regular file.".format(filename))
        sys.exit(1)

all_symbols = []
all_symbols_set = set()

for filename in args.files:
    with open(filename, "r") as f:
        my_symbols = f.read().split()
        for symbol in my_symbols:
            if symbol not in all_symbols_set:
                all_symbols.append(symbol)
                all_symbols_set.add(symbol)

file_template = """\
// This file is generated by mapleall/maplebe/collect/mpl-collect-groot-lists.
// Do not modify manually.
    .section .maple.gcrootsmap, "aw", %progbits
    .p2align 3
    .global {list_name}
    .type   {list_name}, @object
{list_name}:
{fields}
    .size   {list_name}, .-{list_name}
    .p2align 3
    .global {list_size_name}
    .type   {list_size_name}, @object
{list_size_name}:
    .quad {list_size}
    .size   {list_size_name}, .-{list_size_name}
"""

field_template = """\
    .quad  {symbol}
"""

#log("Generating file...")

fields_rendered = "".join(field_template.format(symbol=symbol)
        for symbol in all_symbols)

file_rendered = file_template.format(
        list_name       = LIST_NAME,
        fields          = fields_rendered,
        list_size_name  = LIST_SIZE_NAME,
        list_size       = len(all_symbols),
        )

#log("Writing to {} ...".format(args.output))

with open(args.output, "w") as f:
    f.write(file_rendered)

#log("Done.")

#vim: tw=80 ts=4 sw=4 sts=4 et ai
